home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-20 | 17.9 KB | 634 lines | [TEXT/ALFA] |
- # init.tcl --
- #
- # Default system startup file for Tcl-based applications. Defines
- # "unknown" procedure and auto-load facilities.
- #
- # SCCS: @(#) init.tcl 1.86 97/08/08 10:37:39
- #
- # Copyright (c) 1991-1993 The Regents of the University of California.
- # Copyright (c) 1994-1997 Sun Microsystems, Inc.
- # Some copyright (c) 1997 Vince Darley.
-
- set errorCode ""
- set errorInfo ""
-
- if {[info commands tclLog] == ""} {
- proc tclLog {string} {
- message $string
- }
- }
- # so we can write a few things which are Tcl 8.0 compatible
- if {[info tclversion] < 8.0} {
- ;proc namespace {cmd ns script} {if {$script != ""} {uplevel $script}}
- ;proc variable args {uplevel global $args}
- ;proc namesp {var} {}
- } else {
- namespace eval alpha {}
- namespace eval procs {}
- namespace eval index {}
- # used to force some child namespaces into existence
- ;proc namesp {var} {
- if [catch "uplevel global $var"] {
- set ns ""
- while {[regexp "^(::)?($ns\[a-zA-Z_\]+::)" $var ns]} {
- uplevel "namespace eval $ns {}"
- }
- }
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "unknown" --
- #
- # Almost the same as standard Tcl 8 unknown. Since we're on a Mac,
- # I removed the auto_execok flag, and for some reason had to change
- # 'history change $newcmd 0' to 'history change $newcmd'
- # -------------------------------------------------------------------------
- ##
- # unknown --
- # This procedure is called when a Tcl command is invoked that doesn't
- # exist in the interpreter. It takes the following steps to make the
- # command available:
- #
- # 1. See if the autoload facility can locate the command in a
- # Tcl script file. If so, load it and execute it.
- # 2. If the command was invoked interactively at top-level:
- # (a) see if the command exists as an executable UNIX program.
- # If so, "exec" the command.
- # (b) see if the command requests csh-like history substitution
- # in one of the common forms !!, !<number>, or ^old^new. If
- # so, emulate csh's history substitution.
- # (c) see if the command is a unique abbreviation for another
- # command. If so, invoke the command.
- #
- # Arguments:
- # args - A list whose elements are the words of the original
- # command, including the command name.
- proc unknown args {
- global auto_noload env unknown_pending tcl_interactive
- global errorCode errorInfo
-
- # Save the values of errorCode and errorInfo variables, since they
- # may get modified if caught errors occur below. The variables will
- # be restored just before re-executing the missing command.
-
- set savedErrorCode $errorCode
- set savedErrorInfo $errorInfo
- set name [lindex $args 0]
- if ![info exists auto_noload] {
- #
- # Make sure we're not trying to load the same proc twice.
- #
- if [info exists unknown_pending($name)] {
- return -code error "self-referential recursion in \"unknown\" for command \"$name\"";
- }
- set unknown_pending($name) pending;
- set ret [catch {auto_load $name} msg]
- unset unknown_pending($name);
- if {$ret != 0} {
- return -code $ret -errorcode $errorCode \
- "error while autoloading \"$name\": $msg"
- }
- if ![array size unknown_pending] {
- unset unknown_pending
- }
- if $msg {
- set errorCode $savedErrorCode
- set errorInfo $savedErrorInfo
- set code [catch {uplevel 1 $args} msg]
- if {$code == 1} {
- #
- # Strip the last five lines off the error stack (they're
- # from the "uplevel" command).
- #
-
- set new [split $errorInfo \n]
- set new [join [lrange $new 0 [expr [llength $new] - 6]] \n]
- return -code error -errorcode $errorCode \
- -errorinfo $new $msg
- } else {
- return -code $code $msg
- }
- }
- }
- if {([info level] == 1) && ([info script] == "") \
- && [info exists tcl_interactive] && $tcl_interactive} {
- set errorCode $savedErrorCode
- set errorInfo $savedErrorInfo
- if {$name == "!!"} {
- set newcmd [history event]
- } elseif {[regexp {^!(.+)$} $name dummy event]} {
- set newcmd [history event $event]
- } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name dummy old new]} {
- set newcmd [history event -1]
- catch {regsub -all -- $old $newcmd $new newcmd}
- }
- if [info exists newcmd] {
- tclLog $newcmd
- history change $newcmd
- return [uplevel $newcmd]
- }
-
- set ret [catch {set cmds [info commands $name*]} msg]
- if {[string compare $name "::"] == 0} {
- set name ""
- }
- if {$ret != 0} {
- return -code $ret -errorcode $errorCode \
- "error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
- }
- if {[llength $cmds] == 1} {
- return [uplevel [lreplace $args 0 0 $cmds]]
- }
- if {[llength $cmds] != 0} {
- if {$name == ""} {
- return -code error "empty command name \"\""
- } else {
- return -code error \
- "ambiguous command name \"$name\": [lsort $cmds]"
- }
- }
- }
- return -code error "invalid command name \"$name\""
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "auto_load" --
- #
- # I use this separate proc to be closer to the standard Tcl 8 system
- # of unknown-loading.
- # -------------------------------------------------------------------------
- ##
- proc auto_load cmd {
- set f [procs::find $cmd]
- if {$f != ""} {
- uplevel \#0 source [list $f]
- return [expr {[info commands $cmd] != ""}]
- }
- return 0
- }
-
- # auto_mkindex:
- # Regenerate a tclIndex file from Tcl source files. Takes two arguments:
- # the name of the directory in which the tclIndex file is to be placed,
- # and a glob pattern to use in that directory to locate all of the relevant
- # files.
- proc auto_mkindex {dir {files *.tcl}} {
- set oldDir [pwd]
- cd $dir
- append line "# Tcl autoload index file: each line identifies a file (nowrap)\n\n"
- append line "set \"[file tail [string trim [pwd] :]]_index\" \{\n"
-
- set cid [scancontext create]
- scanmatch $cid {^proc[ ]} {
- if {[regexp {^proc[ ]+(("[^"]+")|(\{[^\}]+\})|([^ ]*))} $matchInfo(line) match procName]} {
- append line "$procName "
- }
- }
-
- foreach file [glob $files] {
- watchCursor
- set f ""
- append line "\{[file tail $file]\14 "
- message [file tail $file]
- set fid [open $file]
- scanfile $cid $fid
- close $fid
- append line "\}\n"
- }
-
- scancontext delete $cid
-
- append line "\}\n"
- catch {
- set f [open tclIndexx w]
- puts -nonewline $f $line
- close $f
- }
- cd $oldDir
-
- foreach i [info vars {*_index}] {
- global $i
- unset $i
- }
- }
-
- proc procs::find {cmd} {
- global auto_path
-
- regsub -all {[][\$?^|*+()\{\}]} $cmd {\\&} cmd
- foreach path $auto_path {
- if {![file exists $path]} continue
- set index "[file tail $path]_index"
- global $index
- if {![info exists $index]} {
- if {![file exists "$path:tclIndexx"]} continue
- uplevel \#0 source [list "$path:tclIndexx"]
- }
- if {[regexp "\n\{(\[^\14\]+)\14\[^\n\]* \[\"\{\]?(::)?${cmd}\[\"\}\]? " [set $index] dummy file]} {
- return "$path:$file"
- }
- }
- return ""
- }
- # this proc adds 'dummy' so 'file dirname' works the same
- # way for tcl7.4 and tcl8.0.
- proc alpha::makeAutoPath {{check_dups 1} {skipPrefs 0}} {
- global HOME auto_path
- if $check_dups {
- set lcmd lunion
- } else {
- set lcmd lappend
- }
- foreach dir {SystemCode Modes Menus} {
- $lcmd auto_path $HOME:Tcl:$dir
- foreach d [glob -nocomplain ${HOME}:Tcl:$dir:*:] {
- $lcmd auto_path [file dirname "${d}dummy"]
- }
- }
- if {!$skipPrefs} {
- $lcmd auto_path $HOME:Tcl:Packages
- $lcmd auto_path $HOME:Tcl:UserModifications
- foreach d [glob -nocomplain $HOME:Tcl:Packages:*:] {
- $lcmd auto_path [file dirname "${d}dummy"]
- }
- }
-
- }
-
- # Clean up temporary files:
- proc removeTemporaryFiles {} {
- global PREFS
- if [file exists "$PREFS:tmp"] {
- foreach f [glob -nocomplain "$PREFS:tmp:*"] {
- message "removing [file tail $f]…"
- removeFile $f
- }
- }
- message "all temporary files removed"
- }
- ##
- # -------------------------------------------------------------------------
- #
- # "auto_reset" --
- #
- # After rebuilding indices, Tcl retains its old index information unless
- # we tell it not to.
- # -------------------------------------------------------------------------
- ##
- proc auto_reset {} {
- global auto_path
- foreach path $auto_path {
- if {![file exists $path]} continue
- set index "[file tail $path]_index"
- global $index
- catch {unset $index}
- }
- }
-
- #================================================================================
- # Wonderful procs from Vince Darley (darley@fas.harvard.edu).
- #===============================================================================
-
- proc traceTclProc {} {
- global tclMenu
- if {[llength [traceFunc status]]>2} {
- catch {markMenuItem $tclMenu {traceTclProc…} off}
- catch {enableMenuItem $tclMenu dumpTraces off}
- if {[string length [set data [traceDump]]]} {
- if {[dialog::yesno "Dump traces?"]} {
- dumpTraces [string trimright [lindex [traceFunc status] 3] {,}] $data
- setWinInfo dirty 0
- }
- }
- traceFunc off
- message "Tracing off."
- return
- }
- if {[llength [winNames]] && [string length [set sel [getSelect]]]} {
- if {[info procs $sel] == "$sel"} {
- set func $sel
- } else {
- set func [listpick -L $sel -p {Func Name:} [lsort -ignore [info procs]]]
- }
- } else {
- set func [listpick -p {Func Name:} [lsort -ignore [info procs]]]
- }
- if {![string length $func]} return
- traceFunc on $func ""
- catch {markMenuItem $tclMenu {traceTclProc…} on}
- catch {enableMenuItem $tclMenu dumpTraces on}
- message "Tracing '$func'…"
- }
-
-
- proc dumpTraces {{name ""} {data ""}} {
- if {![string length $name]} {
- set name [string trimright [lindex [traceFunc status] 3] {,}]
- }
- if {![string length $data]} {
- set data [traceDump]
- }
-
- if {![string length $data]} {
- message "Trace buffer empty"
- } else {
- regsub -all {:} $name {.} name
- new -n "* Trace '$name' *" -m Tcl
- insertText $data
- winReadOnly
- }
- }
-
-
- proc rebuildTclIndices {} {
- global auto_path
- set d [pwd]
- foreach dir $auto_path {
- # in case auto_path contains relative directories (bad idea)
- cd
- # if directory exists
- if { ![catch { cd $dir } ] } {
- # if there are any files
- if { ![catch { glob *.*tcl } ] } {
- message "Building [file tail $dir] index…"
- # use 'catch' also in case directory is write-protected
- catch { auto_mkindex : *.*tcl }
- }
- }
- }
- message ""
- cd $d
- # make alpha forget its old information so the new stuff is loaded
- # when required.
- catch {auto_reset}
- }
-
- set alpha::rebuilding 0
-
- proc alpha::rebuildPackageIndices {} {
- set n [lsort -ignore [alpha::package names]]
- alpha::makeIndices
- if {[lsort -ignore [alpha::package names]] == $n} { return }
- global package::loaded
- set unk [lremove -l ${package::loaded} [alpha::package names]]
- set package::loaded [lremove -l ${package::loaded} $unk]
- # update extensions
- package::makeMenu
- # update package menus (uncomment if we change things so
- # the list of package-menus is updated above)
- #menu::buildSome global
- message "Indices and package menu rebuilt."
- }
-
- proc alpha::makeIndices {} {
- # add all new directories to the auto_path
- alpha::makeAutoPath
- set types {index::extension index::mode index::menu index::uninstall \
- index::maintainer index::help index::disable}
- global pkg_file HOME alpha::rebuilding alpha::version
- eval global $types
- catch {eval cache::delete $types}
- foreach type $types {
- catch {unset $type}
- }
- foreach dir [list SystemCode:CorePackages Modes Menus Packages] {
- lappend dirs "${HOME}:Tcl:${dir}:"
- eval lappend dirs [glob -nocomplain "${HOME}:Tcl:${dir}:*:"]
- }
- set alpha::rebuilding 1
- # provide the 'Alpha' package
- alpha::extension Alpha ${alpha::version} {}
- # now scan
- foreach d $dirs {
- lappend dirspats "${d}*.tcl"
- }
- set filenames [eval [list grepnames \
- "^alpha::(menu|mode|extension|package (uninstall|disable|maintainer|help))"] $dirspats]
- catch {
- global rebuild_cmd_count
- while {[set f [lindex $filenames 0]] != ""} {
- set rebuild_cmd_count 1
- while {[lindex $filenames $rebuild_cmd_count] == $f} {
- incr rebuild_cmd_count
- }
- set filenames [lrange $filenames $rebuild_cmd_count end]
- set pkg_file $f
- message "scanning $f…"
- if {[catch {uplevel \#0 [list source $f]} res] != 11} {
- alertnote "Had a problem extracting package information from [file tail $f]"
- }
- }
- unset rebuild_cmd_count
- }
- set alpha::rebuilding 0
- foreach type $types {
- cache::add $type "variable" $type
- if {$type != "index::extension"} { catch {unset $type} }
- }
- unset pkg_file
- message "Package index rebuilt."
- }
-
- # 'exit' kills Alpha without allowing it to save etc.
- # 'quit' is therefore more mac-like
- rename exit ""
- proc exit {} {quit}
-
- proc alpha::error {string} {
- global reportErrors
- if $reportErrors {
- alertnote [string range $string 0 200]
- } else {
- global alpha::errorLog
- append alpha::errorLog $string
- }
- }
-
- proc alpha::errorAlert {text} {
- alertnote $text
- error $text
- }
-
- namespace eval flag {}
-
- # ALWAYS USE THIS PROC
- proc flag::addType {type} {
- global flag::types
- if {[lsearch -exact ${flag::types} $type] == -1} {
- lappend flag::types $type
- }
- }
-
- # NEVER MESS WITH THIS VARIABLE DIRECTLY
- set flag::types [list "flag" "variable" "binding" "menubinding" "file" "io-file"]
- # Note: other types are triggered by vars ending in 'Colour', 'Color',
- # 'Folder', 'Path', 'Mode', 'Sig', or 'SearchPath'
-
- ##
- # -------------------------------------------------------------------------
- #
- # "newPref" --
- #
- # Define a new preference variable/flag. You can call this procedure
- # either with multiple arguments or with a single list of all the
- # arguments. So 'newPref flag Hey ...' or 'newPref {flag Hey ...}'
- # are both fine.
- #
- # 'type' is one of:
- # 'flag' (on/off only), 'variable' (anything), 'binding' (key-combo)
- # 'menubinding' (key-combo which works in a menu), 'file' (input only),
- # 'io-file' (either input or output). Variables whose name ends in
- # Sig, Folder, Path, Mode, Colour, Color or SearchPath (case matters here)
- # are treated differently, but are still considered of type 'variable'.
- # For convenience this proc will map types sig, folder, color, ...
- # into 'variable' for you, _if_ the variable ends with the correct
- # string.
- #
- # 'name' is the var name,
- #
- # 'val' is its default value (which will be ignored if the variable
- # already has a value)
- #
- # 'pkg' is either 'global' to mean a global preference, or the name
- # of the mode or package (no spaces) for which this is a preference.
- #
- # 'pname' is a procedure to call if this preference is changed by
- # the user (no need to setup a trace). This proc is only called
- # for changes made through prefs dialogs or prefs menus created by
- # Alpha's core procs. Other changes are not traced.
- #
- # Depending on the previous values, there are two optional arguments
- # with the following uses:
- #
- # TYPE:
- #
- # variable:
- #
- # 'options' is a list of items from which this preference takes a single
- # item.
- # 'subopt' is any of 'item', 'index', 'varitem' or 'varindex' or 'array', where
- # 'item' indicates the pref is simply an item from the given list
- # of items, 'index' indicates it is an index into that list, and
- # 'var*' indicates 'items' is in fact the name of a global variable
- # which contains the list. 'array' means take one of the values from an array.
- # If no value is given, 'item' is the default
- #
- # binding:
- #
- # 'options' is the name of a proc to which this item should be bound.
- # If options = '1', then we bind to the proc with the same name as
- # this variable. Otherwise we do not perform automatic bindings.
- #
- # 'subopt' indicates whether the binding is mode-specific or global.
- # It should either be 'global' or the name of a mode. If not given,
- # it defaults to 'global' for all non-modes, and to mode-specific for
- # all packages. (Alpha tests if something is a mode by the existence
- # of modeMenus($mode))
- # -------------------------------------------------------------------------
- ##
- proc newPref {vtype {name {}} {val 0} {pkg "global"} {pname ""} {options ""} {subopt ""}} {
- if {$name == {}} { uplevel 1 newPref $vtype}
- global allFlags allVars tclvars modeVars flag::procs \
- flag::type flag::types
- set bad 1
- foreach ty ${flag::types} {
- if {[string first $vtype $ty] == 0} {
- set vtype $ty
- set bad 0
- break
- }
- }
- if $bad {
- foreach ty {SearchPath Folder Path Mode Colour Color Sig} {
- if {[string first $vtype [string tolower $ty]] == 0} {
- if [regexp "${ty}\$" $name] {
- set vtype variable
- set bad 0
- break
- } else {
- error "Type '$vtype' requires the variable's name to end in '$ty'"
- }
- }
- }
- if $bad {error "Unknown type '$vtype' in call to newPref"}
- }
- if {$pkg == "global"} {
- switch -- $vtype {
- "flag" {
- lappend allFlags $name
- }
- "variable" {
- lappend allVars $name
- }
- default {
- set flag::type($name) $vtype
- lappend allVars $name
- }
- }
-
- global $name
- lunion tclvars $name
- if {![info exists $name]} {set $name $val} else { set val [set $name] }
- } else {
- global ${pkg}modeVars
- lunion modeVars $name
-
- if {![info exists ${pkg}modeVars($name)]} {
- set ${pkg}modeVars($name) $val
- } else {
- set val [set ${pkg}modeVars($name)]
- }
- switch -- $vtype {
- "flag" {
- lunion allFlags $name
- }
- "variable" {
- }
- default {
- set flag::type($name) $vtype
- lappend allVars $name
- }
- }
- }
- # handle 'options'
- if {$options != ""} {
- switch -- $vtype {
- "variable" {
- global flag::list
- if {$subopt == ""} { set subopt "item" }
- if {[lsearch -exact "array item index varitem varindex" $subopt] == -1} {
- error "Unknown list element type '$subopt' in call to newPref."
- }
- set flag::list($name) [list $subopt $options]
- }
- "binding" {
- global flag::binding modeMenus
- if [info exists modeMenus($pkg)] {
- if {$subopt == ""} {
- set subopt $pkg
- } else {
- if {$subopt == "global"} { set subopt "" }
- }
- }
- set flag::binding($name) [list $subopt $options]
- if {$options == 1} { set options $name }
-
- catch "bind [keys::toBind $val] [list $options] $subopt"
- }
- }
- }
- # register the 'modify' proc
- if {[string length $pname]} {
- set flag::procs($name) $pname
- }
- }
-
-
- set alpha::patchlevel ""
- append alpha::version ${alpha::patchlevel}
-